package com.tumblr.logico2012.gestures;

import processing.core.PApplet;
import processing.core.PVector;
import SimpleOpenNI.SimpleOpenNI;

/**
 * This class implements the click gesture for both hands.
 * The click is done by throwing the hand towards the camera and holding for a second.
 * The click is aborted if the hand is take back before the timeout, performed if the
 * timeout occurs.
 * The interface IGesture is not fully implemented: the events gestureStarted, gestureEnded
 * and gestureAborted are still abstract. They are implemented in the various instances of this
 * gesture used in the program.
 */
public abstract class IClickGesture implements IGesture {
	protected PApplet p;
	protected int m_userId;
	protected int m_hand;
	protected boolean m_active;
	protected String m_icon;
	protected boolean m_init;
	
	int m_framecounter;
	
	public static final float ANGLE_THRESHOLD = 2.3f;
	public static final float Y_THRESHOLD = 0.4f;
	public static final float DELAY = 1f;
	
	public IClickGesture(int userId, int hand, PApplet processor, String icon) {
		m_userId = userId;
		m_hand = hand;
		m_active = false;
		p = processor;
		m_icon = icon;
		m_init = false;
	}
	
	@Override
	public boolean isActive() { return m_active; }
	
	@Override
	public boolean isInit() { return m_init; }
	
	@Override
	public int getUserId() { return m_userId; }
	
	@Override 
	public String getIcon() { return m_icon; }
	
	@Override
	public boolean showBorderOnSelect() { return true; }
	
	@Override
	public void init(GestureManager manager) {
		// we need the segment elbox-hand and the angle between elbow-hand and elbow-shoulder 
		manager.enableSegment(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND);
		manager.enableAngle(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND,
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_SHOULDER : SimpleOpenNI.SKEL_LEFT_SHOULDER);
		
		m_init = true;
	}

	@Override
	public void setRefractory(GestureManager manager) {
		GestureManager.Segment segment = manager.getSegment(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND);
		if(segment != null) {
			segment.setRefractory();
		}
		GestureManager.Angle angle = manager.getAngle(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_SHOULDER : SimpleOpenNI.SKEL_LEFT_SHOULDER);
		if(angle != null) {
			angle.setRefractory();
		}
	}

	@Override
	public boolean checkForStartEvent(GestureManager manager) {
		GestureManager.Segment segment = manager.getSegment(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND);
		GestureManager.Angle angle = manager.getAngle(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_SHOULDER : SimpleOpenNI.SKEL_LEFT_SHOULDER);
		if(angle != null && segment != null) {
			if(!m_active) {
				if(segment.m_hasPrevious && angle.m_hasPrevious) {
					//PApplet.println("angle: "+angle.m_angle+" - threshold: "+ANGLE_THRESHOLD+" - y: "+segment.m_xy.y+" - threshold: "+Y_THRESHOLD);
					// the angle between elbow-hand and elbow-shoulder must be big (the arm is stretched)
					// and the segment elbow-hand must be about horizontal
					if(angle.m_angle > ANGLE_THRESHOLD && Math.abs(segment.m_xy.y) < Y_THRESHOLD) {
						m_framecounter = 0;
						m_active = true;
						return true;
					}
				}
			}
		}
		
		return false;
	}

	@Override
	public boolean checkForStopEvent(GestureManager manager) {
		GestureManager.Segment segment = manager.getSegment(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND);
		GestureManager.Angle angle = manager.getAngle(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_SHOULDER : SimpleOpenNI.SKEL_LEFT_SHOULDER);
		m_framecounter ++;
		if(angle != null && segment != null) {
			if(m_active) {
				// not(condition for start event) or timeout
				// the gesture ends if the user take back his/her hand, or if s/he holds long enough.
				if(angle.m_hasPrevious && segment.m_hasPrevious) {
					if(angle.m_angle < ANGLE_THRESHOLD || Math.abs(segment.m_xy.y) > Y_THRESHOLD || m_framecounter/p.frameRate > DELAY) {
						m_active = false;
						return true;
					}
				} else {
					m_active = false;
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean isAborted(GestureManager manager) {
		GestureManager.Angle angle = manager.getAngle(m_userId, 
				m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_ELBOW : SimpleOpenNI.SKEL_LEFT_ELBOW,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_SHOULDER : SimpleOpenNI.SKEL_LEFT_SHOULDER);
		if(angle != null) {
			if(angle.m_hasPrevious) {
				PVector pos = new PVector();
				manager.m_context.getJointPositionSkeleton(m_userId,
						m_hand == RIGHT ? SimpleOpenNI.SKEL_RIGHT_HAND : SimpleOpenNI.SKEL_LEFT_HAND,
								pos);

				// aborted if the timeout didn't occur.
				if(m_framecounter/p.frameRate > DELAY)
					return false;
			}
		}
		return true;
	}
}
